package com.trippin.DB.entities;

import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.NotPersistent;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;
import com.google.gson.annotations.Expose;
import com.trippin.DB.entities.AttractionEntity.ShortDetailedAttraction;
import com.trippin.Utils.ScheduledAttractionEntityComparator;
import com.trippin.Utils.ShortDetailedAttractionListComparator;
import com.trippin.Utils.TimeUtil;

@PersistenceCapable
public class TripEntity implements TrippinEntity, Serializable {

	private static final long serialVersionUID = 1L;

	@PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Key key;
	
	@Persistent
	@Expose
	private long id;
	
	@Persistent
	@Expose
	private Set<Long> sharedWith = new HashSet<Long>();
	
	@Persistent
	@Expose
	private Set<Long> pendingShareInvites = new HashSet<Long>();
	
	@Persistent
	@Expose
	private Long userId;
	
	@Persistent
	@Expose
	private Date createdOn;
	
	@Persistent
	@Expose
	private Date lastModifiedOn;
	
	@Persistent
	@Expose
	private String title;
	
	@Persistent
	@Expose
	private String notes = "";
	
	@NotPersistent
	@Expose
	private Set<ShortDetailedAttraction> notScheduledYetAttractions;	
	@Persistent
	private Set<Key> notScheduledYetAttractionsKeys = new HashSet<Key>();
	
	@NotPersistent
	@Expose
	private Set<ScheduledAttractionEntity> scheduleAttractions;
	@Persistent
	private Set<Key> scheduleAttractionsKeys = new HashSet<Key>();
	
	@Persistent
	@Expose
	private int numOfDays;
	
	@Persistent
	@Expose
	private Date start;
	
	@Persistent
	@Expose
	private Date end;
	
	
	public void initTripEntity(long userId,int numOfDays, Date start, Date end) throws IOException {
		this.createdOn = new Date();
		this.lastModifiedOn = new Date();
		this.userId = userId;
		this.id = -1;
		
		TimeUtil.timeValidator(numOfDays,start,end,false);
		
		if(numOfDays > 0) {
			this.numOfDays = numOfDays;
			this.start = null;
			this.end = null;
			
		}
		else {
			this.start = new Date(start.getTime());
			this.end = new Date(end.getTime());
			long diff = end.getTime() - start.getTime();
			this.numOfDays = (int)(diff / 86400000) + 1;
		}
	}

	public long getId() {
		if(id < 0)
			id = key.getId();
		return id;
	}

	public void setId() {
		id = key.getId();
	}
	
	public void addUserToSharedWith(long userId) {
		if(pendingShareInvites == null) {
			pendingShareInvites = new HashSet<Long>();
		}
		if(pendingShareInvites.contains(userId)) {			
			pendingShareInvites.remove(userId);
			if(sharedWith == null) {
				sharedWith = new HashSet<Long>();
			}
			sharedWith.add(userId);
		}
	}
	
	public void removeUserFromSharedWith(long userId) {
		if(sharedWith != null) {
			sharedWith.remove(userId);
		}
		else {
			sharedWith = new HashSet<Long>();
		}
	}
	
	public boolean isUserSharedWith(long userId) {
		if(sharedWith == null) {
			return false;
		}
		return sharedWith.contains(userId);
	}
	
	public Set<Long> getAllUsersSharedWith() {
		return sharedWith;
	}
	
	public void addUserToPendingShareInvites(long userId) {
		if(sharedWith == null) {
			sharedWith = new HashSet<Long>();
		}
		
		if(!sharedWith.contains(userId)) {
			if(pendingShareInvites == null) {
				pendingShareInvites = new HashSet<Long>();
			}
			pendingShareInvites.add(userId);
		}
	}
	
	public void removeUserFromPendingShareInvites(long userId) {
		if(pendingShareInvites != null) {
			pendingShareInvites.remove(userId);
		}
		else {
			pendingShareInvites = new HashSet<Long>();
		}
	}
	
	public boolean isUserPendingShareInvite(long userId) {
		if(pendingShareInvites == null) {
			return false;
		}
		return pendingShareInvites.contains(userId);
	}
	
	public Set<Long> getAllUsersSharingInvites() {
		if(pendingShareInvites == null) {
			pendingShareInvites = new HashSet<Long>();
		}
		return pendingShareInvites;
	}
	
	public Date getCreatedOn() {
		return createdOn;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public long getUserId() {
		return userId;
	}
	
	public void setUserId(long userId) {
		this.userId = userId;
	}
	
	public int getNumOfDays() {
		return numOfDays;
	}

	public void setNumOfDays(int numOfDays) {
		this.numOfDays = numOfDays;
	}

	public Date getStart() {
		return start;
	}

	public void setStart(Date start) {
		this.start = start;
	}

	public Date getEnd() {
		return end;
	}

	public void setEnd(Date end) {
		this.end = end;
	}

	public Date getLastModifiedOn() {
		return lastModifiedOn;
	}

	public String getNotes() {
		return notes;
	}

	public void setNotes(String notes) {
		this.notes = notes;
	}

	//---------------notScheduledYetAttractions  getters and setters-------------------//
	
	public Set<ShortDetailedAttraction> getNotScheduledYetAttractions() {
		checkNulls();
		return this.notScheduledYetAttractions;
	}
	
	public void setNotScheduledYetAttractions(Set<ShortDetailedAttraction> attractions) {
		this.notScheduledYetAttractions = new TreeSet<ShortDetailedAttraction>(new ShortDetailedAttractionListComparator());
		this.notScheduledYetAttractions.addAll(attractions);
	}
	
	public void addNotScheduledYetAttraction(ShortDetailedAttraction attraction) {
		checkNulls();
		this.notScheduledYetAttractions.add(attraction);
	}
	
	public void addNotScheduledYetAttractionKey(Key attractionKey) {
		this.notScheduledYetAttractionsKeys.add(attractionKey);
	}
	
	public void removeNotScheduledYetAttractionKey(long attractionId) {
		for(Key key : notScheduledYetAttractionsKeys) {
			if(key.getId() == attractionId) {
				this.notScheduledYetAttractionsKeys.remove(key);
				break;
			}
		}		
	}
	
	public Set<Key> getNotScheduledYetAttractionsKeys() {
		return notScheduledYetAttractionsKeys;
	}
	
	public Key getNotScheduledYetAttractionsKey(long attrId) {
		for(Key key : notScheduledYetAttractionsKeys) {
			if(key.getId() == attrId) {
				return key;
			}
		}		
		return null;
	}

		
	//---------------scheduleAttractions getters and setters-------------------//
	
	public Set<ScheduledAttractionEntity> getScheduleAttractions() {
		checkNulls();
		return this.scheduleAttractions;
	}
	
	public  void setScheduleAttractions(Set<ScheduledAttractionEntity> scheduleAttractions) {
		this.scheduleAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator());
		this.scheduleAttractions.addAll(scheduleAttractions);
	}
	
	public  void addScheduleAttraction(ScheduledAttractionEntity scheduleAttraction) {
		checkNulls();
		scheduleAttractions.add(scheduleAttraction);
	}
	
	public void addScheduleAttractionKey(Key scheduleAttrKey) {
		this.scheduleAttractionsKeys.add(scheduleAttrKey);
	}
	
	public void removeScheduleAttractionKey(long scheduledAttractionsId) {
		for(Key key : scheduleAttractionsKeys) {
			if(key.getId() == scheduledAttractionsId) {
				this.scheduleAttractionsKeys.remove(key);
				break;
			}
		}
	}
	
	public Set<Key> getScheduleAttractionsKeys() {
		return scheduleAttractionsKeys;
	}
	
	public void updateTrip(TripEntity trip) throws IOException {
		TimeUtil.timeValidator(trip.getNumOfDays(), trip.getStart(), trip.getEnd(),false);
		this.numOfDays = trip.getNumOfDays();
		this.start = trip.getStart();
		this.end = trip.getEnd();
		this.notes = (trip.getNotes() != null ? trip.getNotes() : "");
		this.title = (trip.getTitle() != null ? trip.getTitle() : "");
		this.lastModifiedOn = new Date();
	}
	
	public void checkNulls() {
		if(notScheduledYetAttractions == null) {
			this.notScheduledYetAttractions = new TreeSet<ShortDetailedAttraction>(new ShortDetailedAttractionListComparator());
		}
		
		if(scheduleAttractions == null) {
			this.scheduleAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator());

		}
	}
	
	public boolean isUserAttendAttraction(long attrId) {
		if(notScheduledYetAttractions != null) {
			for (ShortDetailedAttraction sda : notScheduledYetAttractions) {
				if(sda.getId() == attrId) {
					return true;
				}
			}
		}
		return false;		
	}
	
	
	public static class ShortDetailedTrip implements TrippinEntity {

		@Expose
		private Long id;
		
		@Expose
		private Long userId;

		@Expose
		private String title;

		@Expose
		private int numOfDays;
		
		@Expose
		private Date start;
		
		@Expose
		private Date end;
		
		@Expose
		private Date lastModifiedOn;
		
		@Expose
		private Set<Long> sharedWith;

		public ShortDetailedTrip(TripEntity trip) {
			this.id = trip.getId();
			this.userId = trip.getUserId();
			this.title = trip.getTitle();
			this.numOfDays = trip.getNumOfDays();
			this.start = (trip.getStart() != null) ? new Date(trip.getStart().getTime()) : null;
			this.end = (trip.getEnd() != null) ? new Date(trip.getEnd().getTime()) : null;
			this.lastModifiedOn = new Date(trip.getLastModifiedOn().getTime());
			this.sharedWith = new HashSet<Long>();
			if(trip.getAllUsersSharedWith() != null) {
				this.sharedWith.addAll(trip.getAllUsersSharedWith());
			}
		}

		public Long getId() {
			return id;
		}
		
		public Long getUserId() {
			return userId;
		}

		public String getTitle() {
			return title;
		}

		public int getNumOfDays() {
			return numOfDays;
		}

		public Date getStart() {
			return start;
		}

		public Date getEnd() {
			return end;
		}

		public Date getLastModifiedOn() {
			return lastModifiedOn;
		}

	}



}
